<template>
  <div
    class="ui-anchor"
    @touchstart.stop="hang"
    @touchend.stop="drop"
    @mousedown.stop="hang"
    @mouseup.stop="drop"
    @touchmove.stop="iosMove"
  >
    <slot></slot>
  </div>
</template>

<script>
export default {
  name: 'ui-anchor',
  props: {
    width: {
      type: Number,
      default: 0
    },
    height: {
      type: Number,
      default: 0
    },
    parentWidth: {
      type: Number,
      default: 0
    },
    parentHeight: {
      type: Number,
      default: 0
    },
    x: {
      type: Number,
      default: 0
    },
    y: {
      type: Number,
      default: 0
    }
  },
  watch: {
    width(newWidth, oldWidth) {
      if (newWidth < oldWidth) return
      if (this.left === 0) return
      this.parent.width = this.parentWidth || this.elem.parentNode.offsetWidth
      this.parent.height =
        this.parentHeight || this.elem.parentNode.offsetHeight
      if (newWidth > this.parent.width - this.left) {
        const newLeft = this.parent.width - newWidth
        this.left = newLeft < 0 ? 0 : newLeft
        this.elem.style.left = `${this.left}px`
      }
    },
    height(newHeight, oldHeight) {
      if (newHeight < oldHeight) return
      if (this.top === 0) return
      this.parent.width = this.parentWidth || this.elem.parentNode.offsetWidth
      this.parent.height =
        this.parentHeight || this.elem.parentNode.offsetHeight

      if (newHeight > this.parent.height - this.top) {
        const newTop = this.parent.height - this.height
        this.top = newTop
        this.elem.style.top = `${this.top}px`
      }
    },
    x(newLeft) {
      this.elem.style.left = `${newLeft}px`
    },
    y(newRight) {
      this.elem.style.top = `${newRight}px`
    }
  },
  data: () => ({
    shiftY: null,
    shiftX: null,
    left: 0,
    top: 0,
    elem: null,
    isIos: false,
    parent: {
      width: 0,
      height: 0
    }
  }),
  methods: {
    iosMove(e) {
      if (this.isIos) this.elementMove(e)
    },
    elementMove(e) {
      this.$emit('dragging')
      e.preventDefault()
      if (!e.pageX) {
        document.body.style.overflow = 'hidden'
      }
      const x = e.pageX || e.changedTouches[0].pageX
      const y = e.pageY || e.changedTouches[0].pageY
      let newLeft = x - this.shiftX
      let newTop = y - this.shiftY
      const newRight = x - this.shiftX + this.elem.offsetWidth
      const newBottom = y - this.shiftY + this.elem.offsetHeight
      if (newLeft < 0) {
        newLeft = 0
      } else if (newRight > this.parent.width) {
        newLeft = this.parent.width - this.elem.offsetWidth
      } else {
        newLeft = x - this.shiftX
      }
      if (newTop < 0) {
        newTop = 0
      } else if (newBottom > this.parent.height) {
        newTop = this.parent.height - this.elem.offsetHeight
      } else {
        newTop = y - this.shiftY
      }
      this.elem.style.left = `${newLeft}px`
      this.left = newLeft
      this.elem.style.top = `${newTop}px`
      this.top = newTop
    },
    hang(e) {
      this.$emit('activated')
      this.parent.width = this.parentWidth || this.elem.parentNode.offsetWidth
      this.parent.height =
        this.parentHeight || this.elem.parentNode.offsetHeight
      this.shiftX = e.pageX
        ? e.pageX - this.elem.offsetLeft
        : e.changedTouches[0].pageX - this.elem.offsetLeft
      this.shiftY = e.pageY
        ? e.pageY - this.elem.offsetTop
        : e.changedTouches[0].pageY - this.elem.offsetTop
      if (e.pageX) {
        if (this.isIos) {
          this.elem.parentNode.addEventListener('touchmove', this.elementMove)
        } else {
          this.elem.parentNode.addEventListener('mousemove', this.elementMove)
          this.elem.parentNode.addEventListener('mouseleave', this.drop)
        }
      } else {
        this.elem.addEventListener('touchmove', this.elementMove)
      }
    },
    drop() {
      document.body.style.overflow = null
      this.elem.parentNode.removeEventListener(
        'mousemove',
        this.elementMove,
        false
      )
      this.elem.parentNode.removeEventListener(
        'touchmove',
        this.elementMove,
        false
      )
      this.elem.onmouseup = null
      this.elem.ontouchend = null
      this.$emit('dropped', {
        top: this.top,
        left: this.left,
        parent: this.parent
      })
    }
  },
  mounted() {
    this.isIos =
      /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream
    this.elem = this.$el
    this.elem.style.top = `${this.y}px`
    this.elem.style.left = `${this.x}px`
  }
}
</script>

<style>
.ui-anchor {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
</style>
